<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */
declare (strict_types=1);

namespace lflow\access\DB;

use lflow\access\AbstractDBAccess;
use lflow\access\dialect\Dialect;
use lflow\DBAccess;
use lflow\WorkFlowException;
use ReflectionClass;
use think\facade\Db;

/**
 * 数据库访问类
 *
 * @author Mr.April
 * @since  1.0
 */
class PDOAccess extends AbstractDBAccess implements \lflow\DBAccess
{

    /**
     * 是否开启事务
     *
     * @var bool
     */
    private static bool $isTransaction = false;

    /**
     * 实例
     *
     * @var \lflow\DBAccess|null
     */
    private static ?DBAccess $instance = null;

    /**
     * 私有化
     */
    private function __construct()
    {
    }

    /**
     * 类实例话获取
     *
     * @return \lflow\DBAccess
     */
    public static function getConnection(): DBAccess
    {
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * @param object $map
     *
     * @throws \lflow\WorkFlowException
     */
    public function saveOrUpdate(object $map): void
    {
        try {
            $sql  = $map->{self::$KEY_SQL} ?? '';
            $args = $map->{self::$KEY_ARGS} ?? [];
            $this->execute($sql, $args);
        } catch (\Exception $e) {
            throw new WorkFlowException($e);
        }
    }

    /**
     * @param string $sql
     * @param array  $args
     *
     * @return int|null
     */
    public function execute(string $sql, array $args): ?int
    {
        $result = Db::execute($sql, $args);
        return !$result ? null : $result;
    }

    /**
     * @param $sql
     * @param $args
     *
     * @return null
     */
    public function query($sql, $args)
    {
        $stmt = Db::query($sql, $args);
        if (empty($stmt)) {
            return null;
        }
        return $stmt;
    }

    /**
     * @param $clazz
     * @param $sql
     * @param $args
     *
     * @return mixed
     * @throws \lflow\WorkFlowException
     */
    public function queryObject($clazz, $sql, $args): ?object
    {
        try {
            $stmt = Db::query($sql, $args);
            if (empty($stmt)) {
                return null;
            }
            $result = $stmt[0] ?? [];
            $class  = PDOHelper::getClassName($clazz);
            if (empty($class)) {
                return $result;
            }
            return PDOHelper::arrayToSimpleObj($result, $class);
        } catch (\Exception $e) {
            throw new WorkFlowException($e);
        }
    }

    /**
     * 列表查询
     *
     * @param string $clazz 实例类class
     * @param string $sql   sql语句
     * @param array  $args  参数绑定
     *
     * @return array|false
     * @throws \lflow\WorkFlowException
     */
    public function queryList(string $clazz, string $sql, array $args = []): array
    {
        // 其实就是调用PDOStatment对象里面的fetchAll方法
        try {
            $stmt   = Db::query($sql, $args);
            $result = [];
            if (empty($stmt)) {
                return [];
            }
            $class = PDOHelper::getClassName($clazz);
            if (empty($class)) {
                return $stmt;
            }
            foreach ($stmt as $key => $value) {
                $result[$key] = PDOHelper::arrayToSimpleObj($value, $class);
            }
            return $result;
        } catch (\Exception $e) {
            throw new WorkFlowException($e);
        }
    }

    /**
     * @param $sql
     * @param $args
     *
     * @return int
     * @throws \think\Exception
     */
    public function queryCount($sql, $args): int
    {
        try {
            $stmt = Db::query($sql, $args);
            if (empty($stmt)) {
                return 0;
            }
            return count($stmt);
        } catch (\Exception $e) {
            throw new WorkFlowException($e);
        }
    }

    public function getLatestProcessVersion(string $name): int
    {
        $where  = " where name = ?";
        $result = Db::query(self::$QUERY_VERSION . $where, [$name]);
        return isset($result[0]['max(version)']) ? (int)$result[0]['max(version)'] : 0;
    }

    public function dialect(): Dialect
    {
        return $this->getDialect();
    }

    /**
     * beginTransaction
     * 启动事务
     */
    public function beginTransaction(): void
    {
        if (!self::$isTransaction) {
            Db::startTrans();
            self::$isTransaction = true;
        }
    }

    /**
     * rollback
     * 事务回滚
     */
    public function rollback()
    {
        if (self::$isTransaction) {
            Db::rollback();
            self::$isTransaction = false;
        }
    }

    /**
     * commit
     * 事务提交
     */
    public function commit()
    {
        if (self::$isTransaction) {
            Db::commit();
            self::$isTransaction = false;
        }
    }

    /**
     * 访问一个不可访问的对象方法的时候触发
     *
     * @param $name     [属性名]
     * @param $argument [参数列表]
     *
     * @return mixed
     * @throws \think\Exception
     */
    public function __call($name, $argument)
    {
        throw new WorkFlowException("对不起,您访问的" . $name . "()方法不存在!");
    }

    /**
     * 访问一个不可访问的类方法(静态方法)的时候触发
     *
     * @param $name     [属性名]
     * @param $argument [参数列表]
     *
     * @throws \think\Exception
     */
    public static function __callStatic($name, $argument): void
    {
        throw new WorkFlowException("对不起,您访问的" . $name . "()静态方法不存在!");
    }
}